﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Timers;    //ONLY FOR TESTING

using KnowledgeBase;
using KnowledgeBaseIntermediary;
using LisaConfigurator;

namespace InputHandler
{
    /// <summary>
    /// This class is the implementation of the IInputHandler interface as well as the IInputHandlerDebug interface.
    /// It contains references to the UserCommandHandler and the EnvironmentEventHandler. It informs the using component
    /// of any user commands and environment events received. If a state change occurs, it relays the state change to the UserCommandHandler.
    /// </summary>
    public class InputHandlerImpl:IInputHandler, IInputHandlerDebug
    {
        //Events
        public event HandleInput commandReceived;
        public event HandleInput environmentEventReceived;

        //Other private variables
        Thread inputHandlerThread;
        Boolean running;
        UserCommandHandler userCommandHandler;
        EnvironmentEventHandler environmentEventHandler;
        IConfigurationAccessor lisaConfigurator;

        #region Methods implemented from IInputHandler interface
        
        public void initialize(IKnowledgeBase knowledgeBase, IConfigurationAccessor lisaConfigurator)
        {
            Console.WriteLine("Initializing InputHandler...");
            inputHandlerThread = new Thread(run);
            running = false;
            this.lisaConfigurator = lisaConfigurator;
            userCommandHandler = new UserCommandHandler(knowledgeBase,lisaConfigurator);
            userCommandHandler.userCommandReceived += new HandleInput(userCommandHandler_userCommandReceived);
            environmentEventHandler = new EnvironmentEventHandler(127001, 6768);
            environmentEventHandler.udpEnvironmentEventReceived += new HandleInput(environmentEventHandler_udpEnvironmentEventReceived);
        }
        
        public void start()
        {
            Console.WriteLine("Starting InputHandler...");
            inputHandlerThread.Start();
            running = true;
        }
        
        public void stop()
        {
            Console.WriteLine("Stopping InputHandler...");
            running = false;
        }

        public void changeState(StateDTO newState)
        {
            userCommandHandler.changeState(newState);
        }

        #endregion

        #region Private helper methods
        void run()
        {
            while (running)
            {
                Thread.Sleep(20);
            }
        }

        #endregion

        #region Methods implemented from the IInputHandlerDebug interface
        /// <summary>
        /// Simulates the receipt of a user command at the InputHandler. This is useful for GUIs that want to test and
        /// debug the InputHandler by simulating receipts of UserCommands.
        /// </summary>
        /// <param name="command">The received command.</param>
        public void simulateUserCommandReceipt(String command)
        {
            FireCommandReceived(new InputEventArgs(command));
        }

        /// <summary>
        /// Simulates the receipt of an environment event at the InputHandler. This is useful for GUIs that want to
        /// test and debug the InputHandler by simulating receipts of EnvironmentEvents.
        /// </summary>
        /// <param name="eventString">The environment event received.</param>
        public void simulateEnvironmentEventReceipt(String eventString)
        {
            FireEnvironmentEventReceived(new InputEventArgs(eventString));
        }
        #endregion

        #region Event-related methods
        private void FireCommandReceived(InputEventArgs eventArgs)
        {
            // Get local event for thread safety purposes
            HandleInput handler = commandReceived;
            FireHandleInputEvent(handler,eventArgs);
        }

        private void FireEnvironmentEventReceived(InputEventArgs eventArgs)
        {
            // Get local event for thread safety purposes
            HandleInput handler = environmentEventReceived;
            FireHandleInputEvent(handler, eventArgs);
        }

        private void FireHandleInputEvent(HandleInput handler, InputEventArgs eventArgs)
        {
            if (handler != null)
            {
                foreach (HandleInput singleCast in handler.GetInvocationList())
                {
                    ISynchronizeInvoke syncInvoke = singleCast.Target as ISynchronizeInvoke;
                    try
                    {
                        if (syncInvoke != null && syncInvoke.InvokeRequired)
                        {
                            // Invokie the event on the main thread
                            syncInvoke.Invoke(handler, new object[] { eventArgs });
                        }
                        else
                        {
                            // Raise the event
                            singleCast(eventArgs);
                        }
                    }
                    catch
                    {
                    }
                }
            }
        }

        void userCommandHandler_userCommandReceived(InputEventArgs args)
        {
            FireCommandReceived(args);
        }

        void environmentEventHandler_udpEnvironmentEventReceived(InputEventArgs args)
        {
            FireEnvironmentEventReceived(args);
        }
        #endregion
    }
}
